package com.gjy.util;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.monitor.FileAlterationListener;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.Comparator;
import java.util.List;

/**
 * @author 宫静雨
 * @version 1.0
 * @since 2023-01-19 08:32:25
 */
public class FileUtil {

    private static final Logger logger = LoggerFactory.getLogger(FileUtil.class);

    public static boolean identical(Path f1, Path f2) throws IOException {
        InputStream i1 = Files.newInputStream(f1);
        InputStream i2 = Files.newInputStream(f2);

        String s1 = DigestUtils.md5Hex(i1);
        String s2 = DigestUtils.md5Hex(i2);

        return s1.equals(s2);
    }

    /**
     * 将分片文件目录合并为一个文件
     *
     * @param chunkFolder 分片目录
     * @param mergeFile   合并后的文件
     */
    public static void mergeChunk(Path chunkFolder, Path mergeFile) throws IOException {
        if (Files.exists(mergeFile)) {
            Files.delete(mergeFile);
        }
        Files.createFile(mergeFile);

        RandomAccessFile chunkWrite = new RandomAccessFile(mergeFile.toFile(), "rw");
        chunkWrite.seek(0);
        byte[] buffer = new byte[1024 * 1024];
        Files.list(chunkFolder)
                .sorted(Comparator.comparing(f -> Integer.parseInt(f.getFileName().toString())))
                .forEach(p -> {
                    try {
                        RandomAccessFile chunkRead = new RandomAccessFile(p.toFile(), "rw");
                        int len;
                        while ((len = chunkRead.read(buffer)) != -1) {
                            chunkWrite.write(buffer, 0, len);
                        }
                        chunkRead.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                });
        chunkWrite.close();
    }

    /**
     * 将一个文件按照指定大小分片
     *
     * @param source    源文件
     * @param path      切片目录
     * @param chunkSize 切片大小,字节为单位
     */
    public static void chunk(Path source, Path path, long chunkSize) throws IOException {
        if (!Files.exists(path)) {
            Files.createDirectories(path);
        }
        // 分块数量
        long chunkNum = (long) Math.ceil(Files.size(source) * 1.0 / chunkSize);
        logger.info("分块总数: {}", chunkNum);

        // 缓冲区大小
        byte[] buffer = new byte[1024 * 1024];
        RandomAccessFile chunkRead = new RandomAccessFile(source.toFile(), "r");
        for (long i = 0; i < chunkNum; i++) {
            Path chunkFile = path.resolve(String.valueOf(i));
            if (Files.exists(chunkFile)) {
                Files.delete(chunkFile);
            }
            Files.createFile(chunkFile);
            RandomAccessFile chunkWrite = new RandomAccessFile(chunkFile.toFile(), "rw");
            int len;
            while ((len = chunkRead.read(buffer)) != -1) {
                chunkWrite.write(buffer, 0, len);
                if (Files.size(chunkFile) >= chunkSize) {
                    break;
                }
            }
            chunkWrite.close();
            logger.info("完成分块: {}", i);
        }
        chunkRead.close();
    }

    public static void write(String filename, String content) {
        try {
            Path path = Paths.get(filename);
            Files.write(path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static class FileListener extends FileAlterationListenerAdaptor {

        @Override
        public void onStart(FileAlterationObserver observer) {
            super.onStart(observer);
            logger.info("开始监听: OnStart");
        }

        @Override
        public void onDirectoryCreate(File directory) {
            super.onDirectoryCreate(directory);
            logger.info("目录创建: {}", directory.getAbsolutePath());
        }

        @Override
        public void onDirectoryChange(File directory) {
            super.onDirectoryChange(directory);
            logger.info("目录修改: {}", directory.getAbsolutePath());
        }

        @Override
        public void onDirectoryDelete(File directory) {
            super.onDirectoryDelete(directory);
            logger.info("目录删除: {}", directory.getAbsolutePath());
        }

        @Override
        public void onFileCreate(File file) {
            super.onFileCreate(file);
            logger.info("文件创建: {}", file.getAbsolutePath());
        }

        @Override
        public void onFileChange(File file) {
            super.onFileChange(file);
            String path = file.getAbsolutePath();
            logger.info("文件修改: {}", path);
            if (path.endsWith("txt")) {
                logger.info("处理TXT文件");
            } else if (path.endsWith("json")) {
                logger.info("处理JSON文件");
            } else if (path.endsWith("xml")) {
                logger.info("处理XML文件");
            }
        }

        @Override
        public void onFileDelete(File file) {
            super.onFileDelete(file);
            logger.info("文件删除: {}", file.getAbsolutePath());
        }

        @Override
        public void onStop(FileAlterationObserver observer) {
            super.onStop(observer);
            logger.info("关闭监听: onStop");
        }
    }

    private static class FileMonitor {

        private final FileAlterationMonitor monitor;

        public FileMonitor(long interval) {
            monitor = new FileAlterationMonitor(interval);
        }

        public void monitor(String path, FileAlterationListener listener) {
            FileAlterationObserver observer = new FileAlterationObserver(new File(path));
            monitor.addObserver(observer);
            observer.addListener(listener);
        }

        public void stop() {
            try {
                monitor.stop();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public void start() throws Exception {
            monitor.start();
        }
    }

    public static void monitorStart(String file) {
        FileMonitor monitor = new FileMonitor(10000L);
        try {
            monitor.monitor(file, new FileListener());
            monitor.start();
        } catch (Exception e) {
            monitor.stop();
            throw new RuntimeException(e);
        }
    }

    public static void monitorStart(String file, Long interval) {
        FileMonitor monitor = new FileMonitor(interval);
        try {
            monitor.monitor(file, new FileListener());
            monitor.start();
        } catch (Exception e) {
            monitor.stop();
            throw new RuntimeException(e);
        }
    }

    public static void monitorStop(FileMonitor monitor) {
        monitor.stop();
    }

    @Deprecated
    @SuppressWarnings("all")
    public static void monitor(Path path) {
        try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
            path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE
                    , StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);

            WatchKey key = watchService.take();
            List<WatchEvent<?>> watchEvents = key.pollEvents();
            while (true) {
                for (WatchEvent<?> event : watchEvents) {
                    if (StandardWatchEventKinds.ENTRY_CREATE == event.kind()) {
                        logger.info("创建: [{}/{}]", path.getFileName(), event.context());
                    }
                    if (StandardWatchEventKinds.ENTRY_MODIFY == event.kind()) {
                        logger.info("修改: [{}/{}]", path.getFileName(), event.context());
                    }
                    if (StandardWatchEventKinds.ENTRY_DELETE == event.kind()) {
                        logger.info("删除: [{}/{}]", path.getFileName(), event.context());
                    }
                }
                key.reset();
            }
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }

    }
}
